/*-------------------------------------------------------------------------
 *
 * user.c
 *	  操作角色（以前称为用户）的命令。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/backend/commands/user.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/seclabel.h"
#include "commands/user.h"
#include "libpq/crypt.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"

/* 可能由 pg_upgrade_support 函数设置 */
Oid			binary_upgrade_next_pg_authid_oid = InvalidOid;


/* GUC 参数 */
int			Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256;

/* 在 CreateRole() 和 AlterRole() 中检查密码的钩子 */
check_password_hook_type check_password_hook = NULL;

static void fc_AddRoleMems(const char *fc_rolename, Oid fc_roleid,
						List *fc_memberSpecs, List *fc_memberIds,
						Oid fc_grantorId, bool fc_admin_opt);
static void fc_DelRoleMems(const char *fc_rolename, Oid fc_roleid,
						List *fc_memberSpecs, List *fc_memberIds,
						bool fc_admin_opt);


/* 检查当前用户是否具有 createrole 权限 */
static bool fc_have_createrole_privilege(void)
{
	return has_createrole_privilege(GetUserId());
}


/*
 * 创建角色
 */
Oid CreateRole(ParseState *fc_pstate, CreateRoleStmt *fc_stmt)
{
	Relation	fc_pg_authid_rel;
	TupleDesc	fc_pg_authid_dsc;
	HeapTuple	fc_tuple;
	Datum		fc_new_record[Natts_pg_authid];
	bool		fc_new_record_nulls[Natts_pg_authid];
	Oid			fc_roleid;
	ListCell   *fc_item;
	ListCell   *fc_option;
	char	   *fc_password = NULL;	/* 用户密码 */
	bool		fc_issuper = false;	/* 是否将用户设为超级用户？ */
	bool		fc_inherit = true; /* 是否自动继承权限？ */
	bool		fc_createrole = false; /* 该用户是否可以创建角色？ */
	bool		fc_createdb = false;	/* 用户是否可以创建数据库？ */
	bool		fc_canlogin = false;	/* 该用户是否可以登录？ */
	bool		fc_isreplication = false;	/* 这是否是一个复制角色？ */
	bool		fc_bypassrls = false;	/* 这是否是一个启用行安全的角色？ */
	int			fc_connlimit = -1; /* 允许的最大连接数 */
	List	   *fc_addroleto = NIL;	/* 要成为该角色的成员的角色 */
	List	   *fc_rolemembers = NIL;	/* 要成为此角色成员的角色 */
	List	   *fc_adminmembers = NIL; /* 要成为此角色管理员的角色 */
	char	   *fc_validUntil = NULL;	/* 登录有效期至何时 */
	Datum		fc_validUntil_datum;	/* 同样，作为 timestamptz 数据 */
	bool		fc_validUntil_null;
	DefElem    *fc_dpassword = NULL;
	DefElem    *fc_dissuper = NULL;
	DefElem    *fc_dinherit = NULL;
	DefElem    *fc_dcreaterole = NULL;
	DefElem    *fc_dcreatedb = NULL;
	DefElem    *fc_dcanlogin = NULL;
	DefElem    *fc_disreplication = NULL;
	DefElem    *fc_dconnlimit = NULL;
	DefElem    *fc_daddroleto = NULL;
	DefElem    *fc_drolemembers = NULL;
	DefElem    *fc_dadminmembers = NULL;
	DefElem    *fc_dvalidUntil = NULL;
	DefElem    *fc_dbypassRLS = NULL;

	/* 默认值可能根据原始语句类型而有所不同 */
	switch (fc_stmt->stmt_type)
	{
		case ROLESTMT_ROLE:
			break;
		case ROLESTMT_USER:
			fc_canlogin = true;
			/* 最终可能希望遗传在此默认为 false */
			break;
		case ROLESTMT_GROUP:
			break;
	}

	/* 从语句节点树中提取选项 */
	foreach(fc_option, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_option);

		if (strcmp(fc_defel->defname, "password") == 0)
		{
			if (fc_dpassword)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dpassword = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "sysid") == 0)
		{
			ereport(NOTICE,
					(errmsg("SYSID can no longer be specified")));
		}
		else if (strcmp(fc_defel->defname, "superuser") == 0)
		{
			if (fc_dissuper)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dissuper = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "inherit") == 0)
		{
			if (fc_dinherit)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dinherit = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "createrole") == 0)
		{
			if (fc_dcreaterole)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcreaterole = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "createdb") == 0)
		{
			if (fc_dcreatedb)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcreatedb = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "canlogin") == 0)
		{
			if (fc_dcanlogin)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcanlogin = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "isreplication") == 0)
		{
			if (fc_disreplication)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_disreplication = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "connectionlimit") == 0)
		{
			if (fc_dconnlimit)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dconnlimit = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "addroleto") == 0)
		{
			if (fc_daddroleto)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_daddroleto = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "rolemembers") == 0)
		{
			if (fc_drolemembers)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_drolemembers = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "adminmembers") == 0)
		{
			if (fc_dadminmembers)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dadminmembers = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "validUntil") == 0)
		{
			if (fc_dvalidUntil)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dvalidUntil = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "bypassrls") == 0)
		{
			if (fc_dbypassRLS)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dbypassRLS = fc_defel;
		}
		else
			elog(ERROR, "option \"%s\" not recognized",
				 fc_defel->defname);
	}

	if (fc_dpassword && fc_dpassword->arg)
		fc_password = strVal(fc_dpassword->arg);
	if (fc_dissuper)
		fc_issuper = boolVal(fc_dissuper->arg);
	if (fc_dinherit)
		fc_inherit = boolVal(fc_dinherit->arg);
	if (fc_dcreaterole)
		fc_createrole = boolVal(fc_dcreaterole->arg);
	if (fc_dcreatedb)
		fc_createdb = boolVal(fc_dcreatedb->arg);
	if (fc_dcanlogin)
		fc_canlogin = boolVal(fc_dcanlogin->arg);
	if (fc_disreplication)
		fc_isreplication = boolVal(fc_disreplication->arg);
	if (fc_dconnlimit)
	{
		fc_connlimit = intVal(fc_dconnlimit->arg);
		if (fc_connlimit < -1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid connection limit: %d", fc_connlimit)));
	}
	if (fc_daddroleto)
		fc_addroleto = (List *) fc_daddroleto->arg;
	if (fc_drolemembers)
		fc_rolemembers = (List *) fc_drolemembers->arg;
	if (fc_dadminmembers)
		fc_adminmembers = (List *) fc_dadminmembers->arg;
	if (fc_dvalidUntil)
		fc_validUntil = strVal(fc_dvalidUntil->arg);
	if (fc_dbypassRLS)
		fc_bypassrls = boolVal(fc_dbypassRLS->arg);

	/* 首先检查一些权限 */
	if (fc_issuper)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to create superusers")));
	}
	else if (fc_isreplication)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to create replication users")));
	}
	else if (fc_bypassrls)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to create bypassrls users")));
	}
	else
	{
		if (!fc_have_createrole_privilege())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to create role")));
	}

	/*
	 * 检查用户是否试图在保留的
	 * “pg_” 命名空间中创建角色。
	 */
	if (IsReservedName(fc_stmt->role))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("role name \"%s\" is reserved",
						fc_stmt->role),
				 errdetail("Role names starting with \"pg_\" are reserved.")));

	/*
	 * 如果使用适当的开关构建，当角色名称违反回归测试
	 * 约定时发出警告。
	 */
#ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
	if (strncmp(fc_stmt->role, "regress_", 8) != 0)
		elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
#endif

	/*
	 * 检查 pg_authid 关系以确保角色尚不存在。
	 */
	fc_pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
	fc_pg_authid_dsc = RelationGetDescr(fc_pg_authid_rel);

	if (OidIsValid(get_role_oid(fc_stmt->role, true)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("role \"%s\" already exists",
						fc_stmt->role)));

	/* 将 validuntil 转换为内部格式 */
	if (fc_validUntil)
	{
		fc_validUntil_datum = DirectFunctionCall3(timestamptz_in,
											   CStringGetDatum(fc_validUntil),
											   ObjectIdGetDatum(InvalidOid),
											   Int32GetDatum(-1));
		fc_validUntil_null = false;
	}
	else
	{
		fc_validUntil_datum = (Datum) 0;
		fc_validUntil_null = true;
	}

	/*
	 * 如果定义了，则调用密码检查钩子
	 */
	if (check_password_hook && fc_password)
		(*check_password_hook) (fc_stmt->role,
								fc_password,
								get_password_type(fc_password),
								fc_validUntil_datum,
								fc_validUntil_null);

	/*
	 * 构建要插入的元组
	 */
	MemSet(fc_new_record, 0, sizeof(fc_new_record));
	MemSet(fc_new_record_nulls, false, sizeof(fc_new_record_nulls));

	fc_new_record[Anum_pg_authid_rolname - 1] =
		DirectFunctionCall1(namein, CStringGetDatum(fc_stmt->role));

	fc_new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(fc_issuper);
	fc_new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(fc_inherit);
	fc_new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(fc_createrole);
	fc_new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(fc_createdb);
	fc_new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(fc_canlogin);
	fc_new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(fc_isreplication);
	fc_new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(fc_connlimit);

	if (fc_password)
	{
		char	   *fc_shadow_pass;
		const char *fc_logdetail = NULL;

		/*
		 * 不允许空密码。Libpq 将空密码视为没有密码，
		 * 甚至不会尝试进行身份验证。但其他客户端可能会尝试，
		 * 所以允许它会造成混淆。通过在指定空字符串时清除
		 * 密码，帐户在所有客户端中始终被锁定。
		 *
		 * 请注意，这仅涵盖存储在数据库中的密码。
		 * 身份验证代码中还有其他检查，以禁止在使用
		 * 从外部系统（如 LDAP 或 PAM）获取密码的身份验证方法时
		 * 使用空密码。
		 */
		if (fc_password[0] == '\0' ||
			plain_crypt_verify(fc_stmt->role, fc_password, "", &fc_logdetail) == STATUS_OK)
		{
			ereport(NOTICE,
					(errmsg("empty string is not a valid password, clearing password")));
			fc_new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
		}
		else
		{
			/* 将密码加密为请求的格式。 */
			fc_shadow_pass = encrypt_password(Password_encryption, fc_stmt->role,
										   fc_password);
			fc_new_record[Anum_pg_authid_rolpassword - 1] =
				CStringGetTextDatum(fc_shadow_pass);
		}
	}
	else
		fc_new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;

	fc_new_record[Anum_pg_authid_rolvaliduntil - 1] = fc_validUntil_datum;
	fc_new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = fc_validUntil_null;

	fc_new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(fc_bypassrls);

	/*
	 * pg_largeobject_metadata 包含 pg_authid.oid，因此我们使用
	 * 二进制升级覆盖。
	 */
	if (IsBinaryUpgrade)
	{
		if (!OidIsValid(binary_upgrade_next_pg_authid_oid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("pg_authid OID value not set when in binary upgrade mode")));

		fc_roleid = binary_upgrade_next_pg_authid_oid;
		binary_upgrade_next_pg_authid_oid = InvalidOid;
	}
	else
	{
		fc_roleid = GetNewOidWithIndex(fc_pg_authid_rel, AuthIdOidIndexId,
									Anum_pg_authid_oid);
	}

	fc_new_record[Anum_pg_authid_oid - 1] = ObjectIdGetDatum(fc_roleid);

	fc_tuple = heap_form_tuple(fc_pg_authid_dsc, fc_new_record, fc_new_record_nulls);

	/*
	 * 在 pg_authid 表中插入新记录
	 */
	CatalogTupleInsert(fc_pg_authid_rel, fc_tuple);

	/*
	 * 前进命令计数器，以便我们可以看到新记录；否则
	 * AddRoleMems 中的测试可能会失败。
	 */
	if (fc_addroleto || fc_adminmembers || fc_rolemembers)
		CommandCounterIncrement();

	/*
	 * 将新角色添加到指定的现有角色中。
	 */
	if (fc_addroleto)
	{
		RoleSpec   *fc_thisrole = makeNode(RoleSpec);
		List	   *fc_thisrole_list = list_make1(fc_thisrole);
		List	   *fc_thisrole_oidlist = list_make1_oid(fc_roleid);

		fc_thisrole->roletype = ROLESPEC_CSTRING;
		fc_thisrole->rolename = fc_stmt->role;
		fc_thisrole->location = -1;

		foreach(fc_item, fc_addroleto)
		{
			RoleSpec   *fc_oldrole = lfirst(fc_item);
			HeapTuple	fc_oldroletup = get_rolespec_tuple(fc_oldrole);
			Form_pg_authid fc_oldroleform = (Form_pg_authid) GETSTRUCT(fc_oldroletup);
			Oid			fc_oldroleid = fc_oldroleform->oid;
			char	   *fc_oldrolename = NameStr(fc_oldroleform->rolname);

			fc_AddRoleMems(fc_oldrolename, fc_oldroleid,
						fc_thisrole_list,
						fc_thisrole_oidlist,
						GetUserId(), false);

			ReleaseSysCache(fc_oldroletup);
		}
	}

	/*
	 * 将指定成员添加到这个新角色中。 adminmembers 获得管理员
	 * 选项，而 rolemembers 则没有。
	 */
	fc_AddRoleMems(fc_stmt->role, fc_roleid,
				fc_adminmembers, roleSpecsToIds(fc_adminmembers),
				GetUserId(), true);
	fc_AddRoleMems(fc_stmt->role, fc_roleid,
				fc_rolemembers, roleSpecsToIds(fc_rolemembers),
				GetUserId(), false);

	/* 新角色的创建后钩子 */
	InvokeObjectPostCreateHook(AuthIdRelationId, fc_roleid, 0);

	/*
	 * 关闭 pg_authid，但在提交之前保持锁定。
	 */
	table_close(fc_pg_authid_rel, NoLock);

	return fc_roleid;
}


/*
 * 修改角色
 *
 * 注意：此处接受的 rolemembers 选项旨在支持
 * 向后兼容的 ALTER GROUP 语法。尽管可以说
 * “ALTER ROLE role ROLE rolenames”，但我们不对此进行文档说明。
 */
Oid AlterRole(ParseState *fc_pstate, AlterRoleStmt *fc_stmt)
{
	Datum		fc_new_record[Natts_pg_authid];
	bool		fc_new_record_nulls[Natts_pg_authid];
	bool		fc_new_record_repl[Natts_pg_authid];
	Relation	fc_pg_authid_rel;
	TupleDesc	fc_pg_authid_dsc;
	HeapTuple	fc_tuple,
				fc_new_tuple;
	Form_pg_authid fc_authform;
	ListCell   *fc_option;
	char	   *fc_rolename;
	char	   *fc_password = NULL;	/* 用户密码 */
	int			fc_connlimit = -1; /* 允许的最大连接数 */
	char	   *fc_validUntil = NULL;	/* 登录有效期至何时 */
	Datum		fc_validUntil_datum;	/* 同样，作为 timestamptz 数据 */
	bool		fc_validUntil_null;
	DefElem    *fc_dpassword = NULL;
	DefElem    *fc_dissuper = NULL;
	DefElem    *fc_dinherit = NULL;
	DefElem    *fc_dcreaterole = NULL;
	DefElem    *fc_dcreatedb = NULL;
	DefElem    *fc_dcanlogin = NULL;
	DefElem    *fc_disreplication = NULL;
	DefElem    *fc_dconnlimit = NULL;
	DefElem    *fc_drolemembers = NULL;
	DefElem    *fc_dvalidUntil = NULL;
	DefElem    *fc_dbypassRLS = NULL;
	Oid			fc_roleid;

	check_rolespec_name(fc_stmt->role,
						_("Cannot alter reserved roles."));

	/* 从语句节点树中提取选项 */
	foreach(fc_option, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_option);

		if (strcmp(fc_defel->defname, "password") == 0)
		{
			if (fc_dpassword)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dpassword = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "superuser") == 0)
		{
			if (fc_dissuper)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dissuper = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "inherit") == 0)
		{
			if (fc_dinherit)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dinherit = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "createrole") == 0)
		{
			if (fc_dcreaterole)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcreaterole = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "createdb") == 0)
		{
			if (fc_dcreatedb)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcreatedb = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "canlogin") == 0)
		{
			if (fc_dcanlogin)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dcanlogin = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "isreplication") == 0)
		{
			if (fc_disreplication)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_disreplication = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "connectionlimit") == 0)
		{
			if (fc_dconnlimit)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dconnlimit = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "rolemembers") == 0 &&
				 fc_stmt->action != 0)
		{
			if (fc_drolemembers)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_drolemembers = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "validUntil") == 0)
		{
			if (fc_dvalidUntil)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dvalidUntil = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "bypassrls") == 0)
		{
			if (fc_dbypassRLS)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dbypassRLS = fc_defel;
		}
		else
			elog(ERROR, "option \"%s\" not recognized",
				 fc_defel->defname);
	}

	if (fc_dpassword && fc_dpassword->arg)
		fc_password = strVal(fc_dpassword->arg);
	if (fc_dconnlimit)
	{
		fc_connlimit = intVal(fc_dconnlimit->arg);
		if (fc_connlimit < -1)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid connection limit: %d", fc_connlimit)));
	}
	if (fc_dvalidUntil)
		fc_validUntil = strVal(fc_dvalidUntil->arg);

	/*
	 * 扫描 pg_authid 关系以确认用户存在。
	 */
	fc_pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
	fc_pg_authid_dsc = RelationGetDescr(fc_pg_authid_rel);

	fc_tuple = get_rolespec_tuple(fc_stmt->role);
	fc_authform = (Form_pg_authid) GETSTRUCT(fc_tuple);
	fc_rolename = pstrdup(NameStr(fc_authform->rolname));
	fc_roleid = fc_authform->oid;

	/*
	 * 想要以任何方式干扰超级用户或复制角色，你必须是
	 * 超级用户。我们也坚持需要超级用户来更改 BYPASSRLS
	 * 属性。否则，如果你没有 createrole，只能更改自己的密码。
	 */
	if (fc_authform->rolsuper || fc_dissuper)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to alter superuser roles or change superuser attribute")));
	}
	else if (fc_authform->rolreplication || fc_disreplication)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to alter replication roles or change replication attribute")));
	}
	else if (fc_dbypassRLS)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to change bypassrls attribute")));
	}
	else if (!fc_have_createrole_privilege())
	{
		/* 检查其余部分 */
		if (fc_dinherit || fc_dcreaterole || fc_dcreatedb || fc_dcanlogin || fc_dconnlimit ||
			fc_drolemembers || fc_dvalidUntil || !fc_dpassword || fc_roleid != GetUserId())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied")));
	}

	/* 将 validuntil 转换为内部格式 */
	if (fc_dvalidUntil)
	{
		fc_validUntil_datum = DirectFunctionCall3(timestamptz_in,
											   CStringGetDatum(fc_validUntil),
											   ObjectIdGetDatum(InvalidOid),
											   Int32GetDatum(-1));
		fc_validUntil_null = false;
	}
	else
	{
		/* 获取现有设置以防钩子需要它 */
		fc_validUntil_datum = SysCacheGetAttr(AUTHNAME, fc_tuple,
										   Anum_pg_authid_rolvaliduntil,
										   &fc_validUntil_null);
	}

	/*
	 * 如果定义了，则调用密码检查钩子
	 */
	if (check_password_hook && fc_password)
		(*check_password_hook) (fc_rolename,
								fc_password,
								get_password_type(fc_password),
								fc_validUntil_datum,
								fc_validUntil_null);

	/*
	 * 构建更新的元组，浏览刚获得的信息
	 */
	MemSet(fc_new_record, 0, sizeof(fc_new_record));
	MemSet(fc_new_record_nulls, false, sizeof(fc_new_record_nulls));
	MemSet(fc_new_record_repl, false, sizeof(fc_new_record_repl));

	/*
	 * issuper/createrole/etc
	 */
	if (fc_dissuper)
	{
		fc_new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(boolVal(fc_dissuper->arg));
		fc_new_record_repl[Anum_pg_authid_rolsuper - 1] = true;
	}

	if (fc_dinherit)
	{
		fc_new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(boolVal(fc_dinherit->arg));
		fc_new_record_repl[Anum_pg_authid_rolinherit - 1] = true;
	}

	if (fc_dcreaterole)
	{
		fc_new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(boolVal(fc_dcreaterole->arg));
		fc_new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true;
	}

	if (fc_dcreatedb)
	{
		fc_new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(boolVal(fc_dcreatedb->arg));
		fc_new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true;
	}

	if (fc_dcanlogin)
	{
		fc_new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(boolVal(fc_dcanlogin->arg));
		fc_new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
	}

	if (fc_disreplication)
	{
		fc_new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(boolVal(fc_disreplication->arg));
		fc_new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
	}

	if (fc_dconnlimit)
	{
		fc_new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(fc_connlimit);
		fc_new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
	}

	/* 密码 */
	if (fc_password)
	{
		char	   *fc_shadow_pass;
		const char *fc_logdetail = NULL;

		/* 与 CREATE USER 相同，不允许空密码。 */
		if (fc_password[0] == '\0' ||
			plain_crypt_verify(fc_rolename, fc_password, "", &fc_logdetail) == STATUS_OK)
		{
			ereport(NOTICE,
					(errmsg("empty string is not a valid password, clearing password")));
			fc_new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
		}
		else
		{
			/* 将密码加密为请求的格式。 */
			fc_shadow_pass = encrypt_password(Password_encryption, fc_rolename,
										   fc_password);
			fc_new_record[Anum_pg_authid_rolpassword - 1] =
				CStringGetTextDatum(fc_shadow_pass);
		}
		fc_new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
	}

	/* 取消密码设置 */
	if (fc_dpassword && fc_dpassword->arg == NULL)
	{
		fc_new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
		fc_new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
	}

	/* 有效截止时间 */
	fc_new_record[Anum_pg_authid_rolvaliduntil - 1] = fc_validUntil_datum;
	fc_new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = fc_validUntil_null;
	fc_new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true;

	if (fc_dbypassRLS)
	{
		fc_new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(boolVal(fc_dbypassRLS->arg));
		fc_new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true;
	}

	fc_new_tuple = heap_modify_tuple(fc_tuple, fc_pg_authid_dsc, fc_new_record,
								  fc_new_record_nulls, fc_new_record_repl);
	CatalogTupleUpdate(fc_pg_authid_rel, &fc_tuple->t_self, fc_new_tuple);

	InvokeObjectPostAlterHook(AuthIdRelationId, fc_roleid, 0);

	ReleaseSysCache(fc_tuple);
	heap_freetuple(fc_new_tuple);

	/*
	 * 前进命令计数器，以便我们可以看到新记录；否则
	 * AddRoleMems 中的测试可能会失败。
	 */
	if (fc_drolemembers)
	{
		List	   *fc_rolemembers = (List *) fc_drolemembers->arg;

		CommandCounterIncrement();

		if (fc_stmt->action == +1) /* 向角色添加成员 */
			fc_AddRoleMems(fc_rolename, fc_roleid,
						fc_rolemembers, roleSpecsToIds(fc_rolemembers),
						GetUserId(), false);
		else if (fc_stmt->action == -1)	/* 从角色中删除成员 */
			fc_DelRoleMems(fc_rolename, fc_roleid,
						fc_rolemembers, roleSpecsToIds(fc_rolemembers),
						false);
	}

	/*
	 * 关闭 pg_authid，但在提交之前保持锁定。
	 */
	table_close(fc_pg_authid_rel, NoLock);

	return fc_roleid;
}


/*
 * ALTER ROLE ... SET
 */
Oid AlterRoleSet(AlterRoleSetStmt *fc_stmt)
{
	HeapTuple	fc_roletuple;
	Form_pg_authid fc_roleform;
	Oid			fc_databaseid = InvalidOid;
	Oid			fc_roleid = InvalidOid;

	if (fc_stmt->role)
	{
		check_rolespec_name(fc_stmt->role,
							_("Cannot alter reserved roles."));

		fc_roletuple = get_rolespec_tuple(fc_stmt->role);
		fc_roleform = (Form_pg_authid) GETSTRUCT(fc_roletuple);
		fc_roleid = fc_roleform->oid;

		/*
		 * 获取角色的锁，并确保在此期间角色没有消失。
		 */
		shdepLockAndCheckObject(AuthIdRelationId, fc_roleid);

		/*
		 * 干扰超级用户你必须是超级用户；否则你需要
		 * createrole，或者只是想更改自己的设置
		 */
		if (fc_roleform->rolsuper)
		{
			if (!superuser())
				ereport(ERROR,
						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
						 errmsg("must be superuser to alter superusers")));
		}
		else
		{
			if (!fc_have_createrole_privilege() && fc_roleid != GetUserId())
				ereport(ERROR,
						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
						 errmsg("permission denied")));
		}

		ReleaseSysCache(fc_roletuple);
	}

	/* 查找并锁定指定的数据库 */
	if (fc_stmt->database != NULL)
	{
		fc_databaseid = get_database_oid(fc_stmt->database, false);
		shdepLockAndCheckObject(DatabaseRelationId, fc_databaseid);

		if (!fc_stmt->role)
		{
			/*
			 * 如果未指定角色，则这在实际上与
			 * ALTER DATABASE ... SET 相同，因此使用相同的权限检查。
			 */
			if (!pg_database_ownercheck(fc_databaseid, GetUserId()))
				aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
							   fc_stmt->database);
		}
	}

	if (!fc_stmt->role && !fc_stmt->database)
	{
		/* 必须是超级用户才能全局更改设置。 */
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to alter settings globally")));
	}

	AlterSetting(fc_databaseid, fc_roleid, fc_stmt->setstmt);

	return fc_roleid;
}


/*
 * DROP ROLE
 */
void DropRole(DropRoleStmt *fc_stmt)
{
	Relation	fc_pg_authid_rel,
				fc_pg_auth_members_rel;
	ListCell   *fc_item;

	if (!fc_have_createrole_privilege())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to drop role")));

	/*
	 * 扫描 pg_authid 关系以找到要删除的角色的 Oid。
	 */
	fc_pg_authid_rel = table_open(AuthIdRelationId, RowExclusiveLock);
	fc_pg_auth_members_rel = table_open(AuthMemRelationId, RowExclusiveLock);

	foreach(fc_item, fc_stmt->roles)
	{
		RoleSpec   *fc_rolspec = lfirst(fc_item);
		char	   *fc_role;
		HeapTuple	fc_tuple,
					fc_tmp_tuple;
		Form_pg_authid fc_roleform;
		ScanKeyData fc_scankey;
		char	   *fc_detail;
		char	   *fc_detail_log;
		SysScanDesc fc_sscan;
		Oid			fc_roleid;

		if (fc_rolspec->roletype != ROLESPEC_CSTRING)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("cannot use special role specifier in DROP ROLE")));
		fc_role = fc_rolspec->rolename;

		fc_tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(fc_role));
		if (!HeapTupleIsValid(fc_tuple))
		{
			if (!fc_stmt->missing_ok)
			{
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("role \"%s\" does not exist", fc_role)));
			}
			else
			{
				ereport(NOTICE,
						(errmsg("role \"%s\" does not exist, skipping",
								fc_role)));
			}

			continue;
		}

		fc_roleform = (Form_pg_authid) GETSTRUCT(fc_tuple);
		fc_roleid = fc_roleform->oid;

		if (fc_roleid == GetUserId())
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_IN_USE),
					 errmsg("current user cannot be dropped")));
		if (fc_roleid == GetOuterUserId())
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_IN_USE),
					 errmsg("current user cannot be dropped")));
		if (fc_roleid == GetSessionUserId())
			ereport(ERROR,
					(errcode(ERRCODE_OBJECT_IN_USE),
					 errmsg("session user cannot be dropped")));

		/*
		 * 出于安全原因，我们允许 createrole 持有者删除普通
		 * 角色，但不允许删除超级用户角色。这主要是为了避免
		 * 不小心删除最后一个超级用户的情况。
		 */
		if (fc_roleform->rolsuper && !superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to drop superusers")));

		/* 删除角色的 DROP 钩子 */
		InvokeObjectDropHook(AuthIdRelationId, fc_roleid, 0);

		/*
		 * 锁定角色，以便在我们删除角色时没有人可以为她添加依赖项。
		 * 我们在事务结束之前保持锁定。
		 */
		LockSharedObject(AuthIdRelationId, fc_roleid, 0, AccessExclusiveLock);

		/* 检查依赖于此角色的 pg_shdepend 条目 */
		if (checkSharedDependencies(AuthIdRelationId, fc_roleid,
									&fc_detail, &fc_detail_log))
			ereport(ERROR,
					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
					 errmsg("role \"%s\" cannot be dropped because some objects depend on it",
							fc_role),
					 errdetail_internal("%s", fc_detail),
					 errdetail_log("%s", fc_detail_log)));

		/*
		 * 从 pg_authid 表中移除角色
		 */
		CatalogTupleDelete(fc_pg_authid_rel, &fc_tuple->t_self);

		ReleaseSysCache(fc_tuple);

		/*
		 * 从 pg_auth_members 表中移除角色。我们必须移除所有
		 * 显示它为角色或成员的元组。
		 *
		 * XXX 关于授予者条目怎么办？也许我们应该做一次堆扫描。
		 */
		ScanKeyInit(&fc_scankey,
					Anum_pg_auth_members_roleid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_roleid));

		fc_sscan = systable_beginscan(fc_pg_auth_members_rel, AuthMemRoleMemIndexId,
								   true, NULL, 1, &fc_scankey);

		while (HeapTupleIsValid(fc_tmp_tuple = systable_getnext(fc_sscan)))
		{
			CatalogTupleDelete(fc_pg_auth_members_rel, &fc_tmp_tuple->t_self);
		}

		systable_endscan(fc_sscan);

		ScanKeyInit(&fc_scankey,
					Anum_pg_auth_members_member,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_roleid));

		fc_sscan = systable_beginscan(fc_pg_auth_members_rel, AuthMemMemRoleIndexId,
								   true, NULL, 1, &fc_scankey);

		while (HeapTupleIsValid(fc_tmp_tuple = systable_getnext(fc_sscan)))
		{
			CatalogTupleDelete(fc_pg_auth_members_rel, &fc_tmp_tuple->t_self);
		}

		systable_endscan(fc_sscan);

		/*
		 * 移除此角色上的任何注释或安全标签。
		 */
		DeleteSharedComments(fc_roleid, AuthIdRelationId);
		DeleteSharedSecurityLabel(fc_roleid, AuthIdRelationId);

		/*
		 * 移除此角色的设置。
		 */
		DropSetting(InvalidOid, fc_roleid);

		/*
		 * 预先增加命令计数器，这样循环的后续迭代将
		 * 看到已经做出的更改。这是必不可少的，例如，
		 * 如果我们试图删除一个角色及其直接成员之一——
		 * 如果我们尝试删除连接的 pg_auth_members
		 * 元组两次将会出现错误。（在上面的两个删除循环之间
		 * 我们不需要 CCI，因为一个角色不允许直接包含
		 * 自己。）
		 */
		CommandCounterIncrement();
	}

	/*
	 * 现在我们可以进行清理；但在提交之前保持锁定。
	 */
	table_close(fc_pg_auth_members_rel, NoLock);
	table_close(fc_pg_authid_rel, NoLock);
}

/*
 * 重命名角色
 */
ObjectAddress RenameRole(const char *fc_oldname, const char *fc_newname)
{
	HeapTuple	fc_oldtuple,
				fc_newtuple;
	TupleDesc	fc_dsc;
	Relation	fc_rel;
	Datum		fc_datum;
	bool		fc_isnull;
	Datum		fc_repl_val[Natts_pg_authid];
	bool		fc_repl_null[Natts_pg_authid];
	bool		fc_repl_repl[Natts_pg_authid];
	int			fc_i;
	Oid			fc_roleid;
	ObjectAddress fc_address;
	Form_pg_authid fc_authform;

	fc_rel = table_open(AuthIdRelationId, RowExclusiveLock);
	fc_dsc = RelationGetDescr(fc_rel);

	fc_oldtuple = SearchSysCache1(AUTHNAME, CStringGetDatum(fc_oldname));
	if (!HeapTupleIsValid(fc_oldtuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("role \"%s\" does not exist", fc_oldname)));

	/*
	 * XXX 客户端应用程序可能在某处存储了会话用户，因此
	 * 重命名可能会造成混淆。另一方面，除了有一点混淆之外
	 * 可能没有实际问题，所以请考虑一下并作出决定。
	 * 对于 SET ROLE ... 也是如此，我们并不限制重命名当前
	 * 有效用户 ID。
	 */

	fc_authform = (Form_pg_authid) GETSTRUCT(fc_oldtuple);
	fc_roleid = fc_authform->oid;

	if (fc_roleid == GetSessionUserId())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("session user cannot be renamed")));
	if (fc_roleid == GetOuterUserId())
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("current user cannot be renamed")));

	/*
	 * 检查用户是否试图重命名系统角色，以及是否
	 * 尝试将角色重命名为保留的 "pg_" 命名空间。
	 */
	if (IsReservedName(NameStr(fc_authform->rolname)))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("role name \"%s\" is reserved",
						NameStr(fc_authform->rolname)),
				 errdetail("Role names starting with \"pg_\" are reserved.")));

	if (IsReservedName(fc_newname))
		ereport(ERROR,
				(errcode(ERRCODE_RESERVED_NAME),
				 errmsg("role name \"%s\" is reserved",
						fc_newname),
				 errdetail("Role names starting with \"pg_\" are reserved.")));

	/*
	 * 如果使用适当的开关构建，当角色名称违反回归测试
	 * 约定时发出警告。
	 */
#ifdef ENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
	if (strncmp(fc_newname, "regress_", 8) != 0)
		elog(WARNING, "roles created by regression test cases should have names starting with \"regress_\"");
#endif

	/* 确保新名称不存在 */
	if (SearchSysCacheExists1(AUTHNAME, CStringGetDatum(fc_newname)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("role \"%s\" already exists", fc_newname)));

	/*
	 * createrole 权限足够，除非你想与超级用户打交道
	 */
	if (((Form_pg_authid) GETSTRUCT(fc_oldtuple))->rolsuper)
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to rename superusers")));
	}
	else
	{
		if (!fc_have_createrole_privilege())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to rename role")));
	}

	/* 好的，构造修改后的元组 */
	for (fc_i = 0; fc_i < Natts_pg_authid; fc_i++)
		fc_repl_repl[fc_i] = false;

	fc_repl_repl[Anum_pg_authid_rolname - 1] = true;
	fc_repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
															   CStringGetDatum(fc_newname));
	fc_repl_null[Anum_pg_authid_rolname - 1] = false;

	fc_datum = heap_getattr(fc_oldtuple, Anum_pg_authid_rolpassword, fc_dsc, &fc_isnull);

	if (!fc_isnull && get_password_type(TextDatumGetCString(fc_datum)) == PASSWORD_TYPE_MD5)
	{
		/* MD5 使用用户名作为盐，因此在重命名时只需清除它 */
		fc_repl_repl[Anum_pg_authid_rolpassword - 1] = true;
		fc_repl_null[Anum_pg_authid_rolpassword - 1] = true;

		ereport(NOTICE,
				(errmsg("MD5 password cleared because of role rename")));
	}

	fc_newtuple = heap_modify_tuple(fc_oldtuple, fc_dsc, fc_repl_val, fc_repl_null, fc_repl_repl);
	CatalogTupleUpdate(fc_rel, &fc_oldtuple->t_self, fc_newtuple);

	InvokeObjectPostAlterHook(AuthIdRelationId, fc_roleid, 0);

	ObjectAddressSet(fc_address, AuthIdRelationId, fc_roleid);

	ReleaseSysCache(fc_oldtuple);

	/*
	 * 关闭 pg_authid，但在提交之前保持锁定。
	 */
	table_close(fc_rel, NoLock);

	return fc_address;
}

/*
 * GrantRoleStmt
 *
 * 将角色授予/撤销给/从角色中撤销
 */
void GrantRole(GrantRoleStmt *fc_stmt)
{
	Relation	fc_pg_authid_rel;
	Oid			fc_grantor;
	List	   *fc_grantee_ids;
	ListCell   *fc_item;

	if (fc_stmt->grantor)
		fc_grantor = get_rolespec_oid(fc_stmt->grantor, false);
	else
		fc_grantor = GetUserId();

	fc_grantee_ids = roleSpecsToIds(fc_stmt->grantee_roles);

	/* AccessShareLock 足够，因为我们不修改 pg_authid */
	fc_pg_authid_rel = table_open(AuthIdRelationId, AccessShareLock);

	/*
	 * 遍历所有授予的角色，添加/删除被授予者的条目，
	 * 或者，如果设置了 admin_opt，则仅添加/删除管理员
	 * 选项。
	 *
	 * 注意：权限检查由 AddRoleMems/DelRoleMems 完成
	 */
	foreach(fc_item, fc_stmt->granted_roles)
	{
		AccessPriv *fc_priv = (AccessPriv *) lfirst(fc_item);
		char	   *fc_rolename = fc_priv->priv_name;
		Oid			fc_roleid;

		/* 必须拒绝 priv(columns) 和 ALL PRIVILEGES(columns) */
		if (fc_rolename == NULL || fc_priv->cols != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
					 errmsg("column names cannot be included in GRANT/REVOKE ROLE")));

		fc_roleid = get_role_oid(fc_rolename, false);
		if (fc_stmt->is_grant)
			fc_AddRoleMems(fc_rolename, fc_roleid,
						fc_stmt->grantee_roles, fc_grantee_ids,
						fc_grantor, fc_stmt->admin_opt);
		else
			fc_DelRoleMems(fc_rolename, fc_roleid,
						fc_stmt->grantee_roles, fc_grantee_ids,
						fc_stmt->admin_opt);
	}

	/*
	 * 关闭 pg_authid，但在提交之前保持锁定。
	 */
	table_close(fc_pg_authid_rel, NoLock);
}

/*
 * DropOwnedObjects
 *
 * 删除由给定角色列表拥有的对象。
 */
void DropOwnedObjects(DropOwnedStmt *fc_stmt)
{
	List	   *fc_role_ids = roleSpecsToIds(fc_stmt->roles);
	ListCell   *fc_cell;

	/* 检查权限 */
	foreach(fc_cell, fc_role_ids)
	{
		Oid			fc_roleid = lfirst_oid(fc_cell);

		if (!has_privs_of_role(GetUserId(), fc_roleid))
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to drop objects")));
	}

	/* 好的，执行该操作 */
	shdepDropOwned(fc_role_ids, fc_stmt->behavior);
}

/*
 * ReassignOwnedObjects
 *
 * 将由给定角色列表拥有的对象转移给另一个用户。
 */
void ReassignOwnedObjects(ReassignOwnedStmt *fc_stmt)
{
	List	   *fc_role_ids = roleSpecsToIds(fc_stmt->roles);
	ListCell   *fc_cell;
	Oid			fc_newrole;

	/* 检查权限 */
	foreach(fc_cell, fc_role_ids)
	{
		Oid			fc_roleid = lfirst_oid(fc_cell);

		if (!has_privs_of_role(GetUserId(), fc_roleid))
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied to reassign objects")));
	}

	/* 接收方也必须具有权限 */
	fc_newrole = get_rolespec_oid(fc_stmt->newrole, false);

	if (!has_privs_of_role(GetUserId(), fc_newrole))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied to reassign objects")));

	/* 好的，执行该操作 */
	shdepReassignOwned(fc_role_ids, fc_newrole);
}

/*
 * roleSpecsToIds
 *
 * 给定角色规范的列表，生成相同顺序的角色 OID 列表。
 *
 * 不允许 ROLESPEC_PUBLIC。
 */
List * roleSpecsToIds(List *fc_memberNames)
{
	List	   *fc_result = NIL;
	ListCell   *fc_l;

	foreach(fc_l, fc_memberNames)
	{
		RoleSpec   *fc_rolespec = lfirst_node(RoleSpec, fc_l);
		Oid			fc_roleid;

		fc_roleid = get_rolespec_oid(fc_rolespec, false);
		fc_result = lappend_oid(fc_result, fc_roleid);
	}
	return fc_result;
}

/*
 * AddRoleMems -- 将给定成员添加到指定角色
 *
 * rolename: 要添加到的角色名称（仅用于错误消息）
 * roleid: 要添加到的角色的 OID
 * memberSpecs: 要添加的角色的角色规范列表（仅用于错误消息）
 * memberIds: 要添加的角色的 OID
 * grantorId: 赋予成员资格的人
 * admin_opt: 赋予管理员选项？
 */
static void fc_AddRoleMems(const char *fc_rolename, Oid fc_roleid,
			List *fc_memberSpecs, List *fc_memberIds,
			Oid fc_grantorId, bool fc_admin_opt)
{
	Relation	fc_pg_authmem_rel;
	TupleDesc	fc_pg_authmem_dsc;
	ListCell   *fc_specitem;
	ListCell   *fc_iditem;

	Assert(list_length(fc_memberSpecs) == list_length(fc_memberIds));

	/* 如果没有什么可做的则跳过权限检查 */
	if (!fc_memberIds)
		return;

	/*
	 * 检查权限：必须具有要更改的角色的createrole或admin选项。要操作超级用户角色，您必须是超级用户。
	 */
	if (superuser_arg(fc_roleid))
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to alter superusers")));
	}
	else
	{
		if (!fc_have_createrole_privilege() &&
			!is_admin_of_role(fc_grantorId, fc_roleid))
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must have admin option on role \"%s\"",
							fc_rolename)));
	}

	/*
	 * pg_database_owner的特征是必须有一个隐式的、依赖于情况的成员。这个限制在技术上没有必要。（可以解除这个限制，进一步将pg_database_ownercheck()等同于has_privs_of_role(roleid, ROLE_PG_DATABASE_OWNER)，在这种情况下，明确的、不依赖于情况的成员可以作为任何数据库的所有者。）
	 */
	if (fc_roleid == ROLE_PG_DATABASE_OWNER)
		ereport(ERROR,
				errmsg("role \"%s\" cannot have explicit members", fc_rolename));

	/*
	 * 当前角色成员的授权者的记录并不重要。尽管如此，由于用户可能将其视为粗糙的审计轨迹，仅允许超级用户向第三方推断授权。
	 */
	if (fc_grantorId != GetUserId() && !superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to set grantor")));

	fc_pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
	fc_pg_authmem_dsc = RelationGetDescr(fc_pg_authmem_rel);

	forboth(fc_specitem, fc_memberSpecs, fc_iditem, fc_memberIds)
	{
		RoleSpec   *fc_memberRole = lfirst_node(RoleSpec, fc_specitem);
		Oid			fc_memberid = lfirst_oid(fc_iditem);
		HeapTuple	fc_authmem_tuple;
		HeapTuple	fc_tuple;
		Datum		fc_new_record[Natts_pg_auth_members];
		bool		fc_new_record_nulls[Natts_pg_auth_members];
		bool		fc_new_record_repl[Natts_pg_auth_members];

		/*
		 * pg_database_owner永远不是角色成员。解除此限制将需要对成员循环进行政策决策。可以防止循环，这包括在proposed_datdba是pg_database_owner的成员时使"ALTER DATABASE x OWNER TO proposed_datdba"失败。因此，获得成员资格可能会减少角色所能做的事情。或者，可以允许这些成员资格完成循环。然后，角色可以对自身具有实际的WITH ADMIN OPTION，从而引发对is_admin_of_role()处理该情况的决策。
		 *
		 * 解除此限制对共享对象（数据库和表空间）的所有权也有政策影响。我们允许此类所有权，但我们可能会找到理由在未来禁用它。如果设计这样的禁令，特别麻烦的是，如果设计必须解决pg_database_owner作为拥有共享对象的角色FOO的成员的情况。（这种所有权的效果是，任何其他数据库的所有者都可以作为受影响的共享对象的所有者。）
		 */
		if (fc_memberid == ROLE_PG_DATABASE_OWNER)
			ereport(ERROR,
					errmsg("role \"%s\" cannot be a member of any role",
						   get_rolespec_name(fc_memberRole)));

		/*
		 * 拒绝创建成员循环，包括角色成为自身成员的简单情况。我们通过检查目标角色是否已经是提议成员角色的成员来做到这一点。不过，我们必须忽略可能的超级用户身份，否则我们就无法在超级用户特权角色中授予成员资格。
		 */
		if (is_member_of_role_nosuper(fc_roleid, fc_memberid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
					 errmsg("role \"%s\" is a member of role \"%s\"",
							fc_rolename, get_rolespec_name(fc_memberRole))));

		/*
		 * 检查此角色/成员的条目是否已经存在；如果是，除非我们正在添加admin选项，否则发出警告。
		 */
		fc_authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
										ObjectIdGetDatum(fc_roleid),
										ObjectIdGetDatum(fc_memberid));
		if (HeapTupleIsValid(fc_authmem_tuple) &&
			(!fc_admin_opt ||
			 ((Form_pg_auth_members) GETSTRUCT(fc_authmem_tuple))->admin_option))
		{
			ereport(NOTICE,
					(errmsg("role \"%s\" is already a member of role \"%s\"",
							get_rolespec_name(fc_memberRole), fc_rolename)));
			ReleaseSysCache(fc_authmem_tuple);
			continue;
		}

		/* 构建一个元组以插入或更新 */
		MemSet(fc_new_record, 0, sizeof(fc_new_record));
		MemSet(fc_new_record_nulls, false, sizeof(fc_new_record_nulls));
		MemSet(fc_new_record_repl, false, sizeof(fc_new_record_repl));

		fc_new_record[Anum_pg_auth_members_roleid - 1] = ObjectIdGetDatum(fc_roleid);
		fc_new_record[Anum_pg_auth_members_member - 1] = ObjectIdGetDatum(fc_memberid);
		fc_new_record[Anum_pg_auth_members_grantor - 1] = ObjectIdGetDatum(fc_grantorId);
		fc_new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(fc_admin_opt);

		if (HeapTupleIsValid(fc_authmem_tuple))
		{
			fc_new_record_repl[Anum_pg_auth_members_grantor - 1] = true;
			fc_new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;
			fc_tuple = heap_modify_tuple(fc_authmem_tuple, fc_pg_authmem_dsc,
									  fc_new_record,
									  fc_new_record_nulls, fc_new_record_repl);
			CatalogTupleUpdate(fc_pg_authmem_rel, &fc_tuple->t_self, fc_tuple);
			ReleaseSysCache(fc_authmem_tuple);
		}
		else
		{
			fc_tuple = heap_form_tuple(fc_pg_authmem_dsc,
									fc_new_record, fc_new_record_nulls);
			CatalogTupleInsert(fc_pg_authmem_rel, fc_tuple);
		}

		/* 每次更改后进行CCI，以防列表中存在重复项 */
		CommandCounterIncrement();
	}

	/*
	 * 关闭pg_authmem，但在提交之前保持锁定。
	 */
	table_close(fc_pg_authmem_rel, NoLock);
}

/*
 * DelRoleMems -- 从指定角色中移除给定成员
 *
 * rolename：要删除的角色名称（仅用于错误消息）
 * roleid：要删除的角色的OID
 * memberSpecs：要删除的角色的RoleSpec列表（仅用于错误消息）
 * memberIds：要删除的角色的OID
 * admin_opt：仅移除admin选项？
 */
static void fc_DelRoleMems(const char *fc_rolename, Oid fc_roleid,
			List *fc_memberSpecs, List *fc_memberIds,
			bool fc_admin_opt)
{
	Relation	fc_pg_authmem_rel;
	TupleDesc	fc_pg_authmem_dsc;
	ListCell   *fc_specitem;
	ListCell   *fc_iditem;

	Assert(list_length(fc_memberSpecs) == list_length(fc_memberIds));

	/* 如果没有什么可做的则跳过权限检查 */
	if (!fc_memberIds)
		return;

	/*
	 * 检查权限：必须具有要更改的角色的createrole或admin选项。要操作超级用户角色，您必须是超级用户。
	 */
	if (superuser_arg(fc_roleid))
	{
		if (!superuser())
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must be superuser to alter superusers")));
	}
	else
	{
		if (!fc_have_createrole_privilege() &&
			!is_admin_of_role(GetUserId(), fc_roleid))
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("must have admin option on role \"%s\"",
							fc_rolename)));
	}

	fc_pg_authmem_rel = table_open(AuthMemRelationId, RowExclusiveLock);
	fc_pg_authmem_dsc = RelationGetDescr(fc_pg_authmem_rel);

	forboth(fc_specitem, fc_memberSpecs, fc_iditem, fc_memberIds)
	{
		RoleSpec   *fc_memberRole = lfirst(fc_specitem);
		Oid			fc_memberid = lfirst_oid(fc_iditem);
		HeapTuple	fc_authmem_tuple;

		/*
		 * 查找此角色/成员的条目
		 */
		fc_authmem_tuple = SearchSysCache2(AUTHMEMROLEMEM,
										ObjectIdGetDatum(fc_roleid),
										ObjectIdGetDatum(fc_memberid));
		if (!HeapTupleIsValid(fc_authmem_tuple))
		{
			ereport(WARNING,
					(errmsg("role \"%s\" is not a member of role \"%s\"",
							get_rolespec_name(fc_memberRole), fc_rolename)));
			continue;
		}

		if (!fc_admin_opt)
		{
			/* 完全移除该条目 */
			CatalogTupleDelete(fc_pg_authmem_rel, &fc_authmem_tuple->t_self);
		}
		else
		{
			/* 只需关闭 admin 选项 */
			HeapTuple	fc_tuple;
			Datum		fc_new_record[Natts_pg_auth_members];
			bool		fc_new_record_nulls[Natts_pg_auth_members];
			bool		fc_new_record_repl[Natts_pg_auth_members];

			/* 构建一个元组以进行更新 */
			MemSet(fc_new_record, 0, sizeof(fc_new_record));
			MemSet(fc_new_record_nulls, false, sizeof(fc_new_record_nulls));
			MemSet(fc_new_record_repl, false, sizeof(fc_new_record_repl));

			fc_new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(false);
			fc_new_record_repl[Anum_pg_auth_members_admin_option - 1] = true;

			fc_tuple = heap_modify_tuple(fc_authmem_tuple, fc_pg_authmem_dsc,
									  fc_new_record,
									  fc_new_record_nulls, fc_new_record_repl);
			CatalogTupleUpdate(fc_pg_authmem_rel, &fc_tuple->t_self, fc_tuple);
		}

		ReleaseSysCache(fc_authmem_tuple);

		/* 每次更改后进行CCI，以防列表中存在重复项 */
		CommandCounterIncrement();
	}

	/*
	 * 关闭pg_authmem，但在提交之前保持锁定。
	 */
	table_close(fc_pg_authmem_rel, NoLock);
}
